package com.cmes.system.core.service;

import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.cmes.cache.service.CacheService;
import com.cmes.core.constant.enums.EnableStatus;
import com.cmes.core.utils.CollectionUtils;
import com.cmes.core.utils.MathUtils;
import com.cmes.core.utils.Preconditions;
import com.cmes.jdbc.service.GenericService;
import com.cmes.system.core.constant.ErrorCode;
import com.cmes.system.core.constant.PermissionTypeEnum;
import com.cmes.system.core.entity.PermissionEntity;
import com.cmes.system.core.repository.PermissionRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.PostConstruct;
import java.util.*;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 权限管理
 *
 * @author Bruce.Gong
 * @since 1.0.0-SNAPSHOT
 */
@Slf4j
@RequiredArgsConstructor
@Service
@Transactional(propagation = Propagation.SUPPORTS, rollbackFor = Exception.class)
public class PermissionService extends GenericService<PermissionRepository, PermissionEntity> {

    private static final String CACHED_KEY = "system:permission";
    private final CacheService cacheService;
    private final RolePermissionService rolePermissionService;

    @SuppressWarnings("unused")
    @PostConstruct
    public void init() {
        cacheService.register(CACHED_KEY, "全部权限");
    }

    /**
     * 查询所有有效权限
     */
    public List<PermissionEntity> listActivePermissions() {
        return cacheService.getCache(CACHED_KEY, () -> list(Wrappers.<PermissionEntity>lambdaQuery()
                .eq(PermissionEntity::getStatus, EnableStatus.ENABLE.getCode())));
    }

    /**
     * 查询所有权限，以树结构返回
     *
     * @return 权限树
     */
    public List<PermissionEntity> tree() {
        return CollectionUtils.listTree(list(),
                0L,
                PermissionEntity::getId,
                PermissionEntity::getPid,
                PermissionEntity::setChildren,
                null,
                Comparator.comparing(PermissionEntity::getPid).thenComparing(PermissionEntity::getSort));
    }

    // region 前端页面增删查改操作

    /**
     * 是否可新增
     *
     * @param entity 新增对象
     */
    public void canAdd(PermissionEntity entity) {
        checkParentActive(entity.getPid());
    }

    /**
     * 判断是否可修改
     *
     * @param entity 权限
     * @return 权限信息
     */
    public PermissionEntity canUpdate(PermissionEntity entity) {
        PermissionEntity db = checkActive(entity.getId());
        // 父权限发生变化，检查父权限状态，检查是否有子权限
        boolean parentChanged = !MathUtils.equals(db.getPid(), entity.getPid());
        if (parentChanged) {
            checkParentActive(entity.getPid());
        }
        boolean typeChanged = db.getType().compareTo(entity.getType()) != 0;
        if (typeChanged && PermissionTypeEnum.BUTTON.match(entity.getType())) {
            checkHasChildren(db.getId());
        }
        return db;
    }

    /**
     * 判断是否可删除
     *
     * @param ids 权限ID
     * @return 权限信息
     */
    public Set<Long> canDelete(List<Long> ids) {
        return routerChildren(ids, null);
    }

    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void deletePermission(Set<Long> ids) {
        deleteBatch(ids);
        rolePermissionService.deleteByPermissionIds(ids);
    }

    /**
     * 判断是否可停用
     *
     * @param ids 权限ID
     * @return 权限信息
     */
    public Set<Long> canDisable(List<Long> ids) {
        return routerChildren(ids, EnableStatus.ENABLE);
    }

    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void disable(Set<Long> ids) {
        update(Wrappers.<PermissionEntity>lambdaUpdate()
                .set(PermissionEntity::getStatus, EnableStatus.DISABLE.getCode())
                .in(PermissionEntity::getId, ids)
                .eq(PermissionEntity::getStatus, EnableStatus.ENABLE.getCode()));
    }

    /**
     * 判断是否可启用
     *
     * @param ids 权限ID
     * @return 权限信息
     */
    public Set<Long> canEnable(List<Long> ids) {
        return routerParent(ids);
    }

    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void enable(Set<Long> ids) {
        update(Wrappers.<PermissionEntity>lambdaUpdate()
                .set(PermissionEntity::getStatus, EnableStatus.ENABLE.getCode())
                .in(PermissionEntity::getId, ids)
                .eq(PermissionEntity::getStatus, EnableStatus.DISABLE.getCode()));
    }

    /**
     * 校验权限是否存在
     *
     * @return 权限信息
     */
    private PermissionEntity checkExists(Long id) {
        PermissionEntity db = getById(id);
        Preconditions.isNull(db, ErrorCode.PERM_NOT_EXISTS);
        return db;
    }

    /**
     * 校验权限状态
     * - 权限是否存在
     * - 权限状态是否可用
     *
     * @return 权限信息
     */
    private PermissionEntity checkActive(Long id) {
        PermissionEntity db = checkExists(id);
        Preconditions.check(() -> EnableStatus.DISABLE.match(db.getStatus()), ErrorCode.PERM_DISABLED);
        return db;
    }

    /**
     * 检查父级权限是否可用
     */
    public void checkParentActive(Long id) {
        if (id == null || id.compareTo(0L) == 0) {
            return;
        }
        PermissionEntity db = getById(id);
        Preconditions.isNull(db, ErrorCode.PERM_PARENT_NOT_EXISTS);
        Preconditions.check(() -> EnableStatus.DISABLE.match(db.getStatus()), ErrorCode.PERM_PARENT_DISABLED);
        Preconditions.check(() -> PermissionTypeEnum.BUTTON.match(db.getType()), ErrorCode.PERM_PARENT_BUTTON);
    }

    /**
     * 检查是否存在子权限，如果存在子权限，则不能调整父权限，也不能删除或者停用
     *
     * @param id 权限ID
     */
    private void checkHasChildren(Long id) {
        Preconditions.check(() -> exists(Wrappers.<PermissionEntity>lambdaQuery()
                .eq(PermissionEntity::getPid, id)),
                ErrorCode.PERM_HAS_CHILDREN);
    }

    private Set<Long> routerChildren(List<Long> ids, EnableStatus status) {
        List<PermissionEntity> all = checkStatus(ids, status, ErrorCode.PERMS_DISABLED, (l, m) -> l);
        Set<Long> mergedIds = new HashSet<>(ids);
        Map<Long, List<PermissionEntity>> group = all.stream().collect(Collectors.groupingBy(PermissionEntity::getPid));
        for (Long id : ids) {
            routerChildren(group, id, status, mergedIds);
        }
        return mergedIds;
    }

    private void routerChildren(Map<Long, List<PermissionEntity>> map, Long id, EnableStatus status, Set<Long> mergedIds) {
        List<PermissionEntity> children = map.get(id);
        if (!CollectionUtils.isEmpty(children)) {
            for (PermissionEntity child : children) {
                if (status != null) {
                    if (status.match(child.getStatus())) {
                        mergedIds.add(child.getId());
                        routerChildren(map, child.getId(), status, mergedIds);
                    }
                } else {
                    mergedIds.add(child.getId());
                    routerChildren(map, child.getId(), status, mergedIds);
                }
            }
        }
    }

    private Set<Long> routerParent(List<Long> ids) {
        Map<Long, PermissionEntity> map = checkStatus(ids, EnableStatus.DISABLE, ErrorCode.PERMS_ENABLED, (l, m) -> m);
        Set<Long> mergedIds = new HashSet<>(ids);
        for (Long id : ids) {
            routerParent(map, map.get(id), EnableStatus.DISABLE, mergedIds);
        }
        return mergedIds;
    }

    private void routerParent(Map<Long, PermissionEntity> map, PermissionEntity dept, EnableStatus status, Set<Long> mergedIds) {
        if (dept != null && dept.getPid() > 0L) {
            PermissionEntity parent = map.get(dept.getPid());
            Preconditions.isNull(parent, ErrorCode.PERM_PARENT_NOT_EXISTS);
            if (status != null) {
                if (status.match(parent.getStatus())) {
                    mergedIds.add(parent.getId());
                    routerParent(map, parent, status, mergedIds);
                }
            } else {
                mergedIds.add(parent.getId());
                routerParent(map, parent, status, mergedIds);
            }
        }
    }

    private <T> T checkStatus(List<Long> ids, EnableStatus status, ErrorCode errorCode,
                              BiFunction<List<PermissionEntity>, Map<Long, PermissionEntity>, T> func) {
        List<PermissionEntity> all = list();
        Map<Long, PermissionEntity> map = all.stream().collect(Collectors.toMap(PermissionEntity::getId, Function.identity()));
        for (Long id : ids) {
            PermissionEntity current = map.get(id);
            Preconditions.isNull(current, ErrorCode.PERM_NOT_EXISTS);
            if (status != null) {
                Preconditions.check(() -> !status.match(current.getStatus()), errorCode);
            }
        }
        return func.apply(all, map);
    }

    // endregion

    // region 缓存操作

    public void removeCache() {
        try {
            cacheService.removeCache(CACHED_KEY);
        } catch (Exception e) {
            log.error("删除缓存失败[permission]", e);
        }
    }

    // endregion
}
